Spring MVC是Spring的一个重要模块,在使用Spring MVC的时候,需要在web.xml中配置DispatcherServlet,可以把它看成一个前端控制器的具体实现,还需要在Bean定义中配置Web请求和Controller(控制器)的对应关系,以及各种视图的展现方式。具体流程如下图所示。从中可以看出核心类是DispatcherServlet.

用文字描述执行流程如下:

  1. 用户发送请求到前端控制器DispatcherServlet,
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成),一并返回DispatcherServlet.
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器。
  5. HandlerAdapter执行处理器(handler,也叫后端控制器)
  6. Controller执行完成返回ModelAndView
  7. HandlerAdapter将handler执行结果ModelAndView返回至DispatcherServlet.
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View对象
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
  11. DispatcherServlet响应用户

Spring MVC框架的启动

SpringMVC的核心是DispatchServlet,它是前端控制器,负责拦截客户端发过来的请求,然后解析请求进行分发。
DispatchServlet持有一个以自己的Servlet名称命名的IOC容器,它是一个WebApplicationContext对象。
Web容器启动时会加载DispatchServlet,每个Servlet在第一次加载时都会调用init()方法,但是DispatcherServlet本身没有这个方法,所以系统会去它父类寻找init()方法,最后在HttpServletBean中找到,调用。
HttpServletBean的init()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Override public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
//获取Servlet的初始化参数,对Bean属性进行配置
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}

// Let subclasses do whatever initialization they like.
//调用子类的initServletBean进行具体的初始化,在该类中它是有一个空方法,没有具体的实现。
//该处调用的是FrameworkServlet类的
initServletBean(); if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}


//子类可以重写此项来执行自定义的初始化
//在调用此方法之前,将设置此servlet的所有bean属性。
/**
* Subclasses may override this to perform custom initialization. * All bean properties of this servlet will have been set before this * method is invoked. * <p>This default implementation is empty.
* @throws ServletException if subclass initialization fails
*/ protected void initServletBean() throws ServletException {
}

FrameworkServlet的initServletBean()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始化上下文
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}

if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}

this.webApplicationContext = initWebApplicationContext();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
protected WebApplicationContext initWebApplicationContext() {
//首先通过ServletContext获得Spring容器,因为子容器SpringMvc要和父容器进行关联
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//定义Springmvc容器wac
WebApplicationContext wac = null;
//判断容器是否由编程式传入(即是否已经存在了容器实例),存在的话直接赋值给wac.
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//在ServletContext中寻找是否有SpringMVC容器,初次运行是没有的
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
//当war即没有被编程式注册到容器中,也没有在ServletContext找到,此时就要新建一个。
wac = createWebApplicationContext(rootContext);
}

if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) {
//到这里mvc的容器已经创建完毕,接着才是真正调用DispatcherServlet的初始化方法onRefresh
onRefresh(wac);
}
}

if (this.publishContext) {
// Publish the context as a servlet context attribute.
//将SpringMVC容器存放到ServletContext中去,方便下次取出来。
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}

return wac; }

DispatcherServlet的refresh()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected void initStrategies(ApplicationContext context) {
//文件上传解析
initMultipartResolver(context);
//本地解析
initLocaleResolver(context);
//主题解析
initThemeResolver(context);
//url请求解析
initHandlerMappings(context);
//初始化真正调用controller方法的类
initHandlerAdapters(context);
//异常解析
initHandlerExceptionResolvers(context);
//视图解析
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context); }

其中,initHandlerMappings中把在Bean配置文字中配置好的handlerMapping从Ioc容器中取得。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null; if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//这里导入所有的HandlerMapping Bean,这些Bean可以在当前的DispatcherServlet的IOC容器中,也可能在其双亲上下文
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
//可以根据名称从当前的IOC容器中通过getBean
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}

//如果没有找到handermappings,那么需要为Servlet设定默认的handerMappings,这些默认的值可以设置在DispatcherServlet.properties中
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}

整体流程简介:
web应用启动时扫描web.xml文件,扫描到DispatcherServlet,对其进行初始化调用DispatcherServlet父类的父类HttpServletBean的init()方法,把配置DispatcherServlet的初始化参数设置到DispatcherServlet中,调用子类FrameworkServlet中的initServletBean()方法,该方法创建SpringMVC容器实例并初始化容器,并且与spring父容器进行关联,使得mvc容器能访问Spring容器里的bean,之后调用子类DispatcherServlet的onRefresh()方法。

HandlerMapping

Spring MVC的Control主要由HandlerMapping和HandlerAdapter两个组件提供。
HandlerMapping负责映射用户的URL和对应的处理类,HandlerMapping并没有规定这个URL与应用的处理类如何映射,在HandlerMapping接口中只定义了根据一个URL必须返回由HandlerExecutionChain代表的处理链,我们可以在这个处理链上添加任意的HandlerAdapter实例来处理这个URL对用的请求。
简单的说,HandlerMapping的作用就是根据当前请求找到对应的Handler,并将Handler(执行程序)与一堆HandlerInterceptor(拦截器)封装到HandlerExecutionChain对象中。
HandlerMapping是由DispatcherServlet调用, DispatcherServlet会从容器中取出所有HandlerMapping实例进行遍历,让HandlerMapping实例根据自己实现类的方式去尝试查找Handler。